package com.atxiaodei.javajuc.jucdemo;

import org.junit.Test;

import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.stream.LongStream;

/**
 * @author wangmeng
 * @date 2020/9/23
 * ForkJoinPool 分支/合并框架 工作窃取
 * 前言：在工作当中我们在计算一个很大值加上一个很大的值等等计算的时候，这时候计算机在串行计算的时候就很耗CPU，效率也很低，这时候我们可以选择
 * 多线程分支合并 利用多个线程去计算，然后将最后各个线程的结果合并最终的结果
 **/
public class TestForkJoinPool线程分支合并 {

    public static void main(String[] args) {
        // 1.8 特性获取计算
        Instant start = Instant.now();

        ForkJoinPool pool = new ForkJoinPool();

        ForkJoinTask<Long> task = new ForkJoinSumCalculate(0L, 50000000000L);

        Long sum = pool.invoke(task);

        System.out.println(sum);

        Instant end = Instant.now();

        System.out.println("耗费时间为：" + Duration.between(start, end).toMillis());//166-1996-10590
    }

    @Test
    public void test1(){
        Instant start = Instant.now();

        long sum = 0L;

        for (long i = 0L; i <= 50000000000L; i++) {
            sum += i;
        }

        System.out.println(sum);

        Instant end = Instant.now();

        System.out.println("耗费时间为：" + Duration.between(start, end).toMillis());//35-3142-15704
    }

    //java8 新特性
    @Test
    public void test2(){
        Instant start = Instant.now();

        Long sum = LongStream.rangeClosed(0L, 50000000000L)
                .parallel()
                .reduce(0L, Long::sum);

        System.out.println(sum);

        Instant end = Instant.now();

        System.out.println("耗费时间为：" + Duration.between(start, end).toMillis());//1536-8118
    }

}

// 继承 RecursiveTask<返回类型> 实现compute方法完成分支合并
class ForkJoinSumCalculate extends RecursiveTask<Long> {

    private static final long serialVersionUID = -259195479995561737L;

    private long start;
    private long end;

    private static final long THURSHOLD = 10000L;  //临界值

    public ForkJoinSumCalculate(long start, long end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        long length = end - start;

        if(length <= THURSHOLD){
            long sum = 0L;

            for (long i = start; i <= end; i++) {
                sum += i;
            }

            return sum;
        }else{
            long middle = (start + end) / 2;

            ForkJoinSumCalculate left = new ForkJoinSumCalculate(start, middle);
            //进行拆分（这里看不到到底在哪个里面计算的值呢？好好想，好看，
            // 上面的概念，（拆到不可再拆时，也就是当前length小于等于10000L的时候进行计算）），同时压入线程队列
            left.fork();

            ForkJoinSumCalculate right = new ForkJoinSumCalculate(middle+1, end);
            right.fork(); //进行拆分，同时压入线程队列

            return left.join() + right.join();
        }
    }

}
